package com.haiqiu.common.utils.files;

import lombok.Data;
import com.haiqiu.common.exception.BaseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author haiqiu
 * Ftp工具类
 */
@Component
@Data
public class FtpUtil {
    private final Logger LOGGER = LoggerFactory.getLogger(FtpUtil.class);

    /**
     * 连接ftp配置
     */
    @Value("${ftp.host:zenka.love}")
    private String host;
    @Value("${ftp.port:21}")
    private int port;
    @Value("${ftp.username:ftp}")
    private String username;
    @Value("${ftp.password:ftp}")
    private String password;

    /**
     * ftp主目录
     */
    @Value("${ftp.home:/}")
    private String home;
    /**
     * ftp绝对根路径
     */
    @Value("${ftp.path:/home/ftpUser}")
    private String path;
    @Value("${ftp.server:https://zenka.love/resource}")
    private String server;

    /**
     * 本地字符编码
     */
    static String LOCAL_CHARSET = "GBK";

    /**
     * FTP协议里面，规定文件名编码为iso-8859-1
     */
    static String SERVER_CHARSET = "ISO-8859-1";


    /**
     * 超时时间
     */
    private static final int DEFAULT_TIMEOUT = 60 * 1000;

    /**
     * 实例化ftpClient对象
     */
    FTPClient ftpClient = new FTPClient();

    /**
     * ftp连接登录方法
     */
    public void connect() {
        //初始化参数配置
        //设置为被动模式
//        ftpClient.enterLocalPassiveMode();
        //设置为主动模式
//        ftpClient.enterLocalActiveMode();
        //设置超时时间
        ftpClient.setConnectTimeout(DEFAULT_TIMEOUT);
        try {
            ftpClient.connect(host, port);
            LOGGER.info(String.format("ftp连接服务器：%s，端口：%d成功", host, port));
            ftpClient.login(username, password);
            LOGGER.info(String.format("ftp登录服务器用户名：%s，密码：%s成功", username, password));
            //返回状态码
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                throw new IOException("Can't connect to server :" + host);
            }
            //设置文件类型为二进制文件
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            // 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
//            if (FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
//                LOCAL_CHARSET = "UTF-8";
//            }
            ftpClient.setControlEncoding(LOCAL_CHARSET);
        } catch (IOException e) {
            e.printStackTrace();
            throw new BaseException(e.getMessage());
        }
    }


    /**
     * ftp上传文件
     *
     * @param multipartFile 文件
     * @param folder        目标文件夹
     * @return 文件url地址
     * @throws IOException 异常
     */
    public String uploadFile(MultipartFile multipartFile, String folder) throws IOException {
        InputStream inputStream = null;
//        BufferedInputStream inputStream = null;
        try {
            //ftp未连接重新连接
            isConnect();
            LOGGER.info("获取到文件" + multipartFile.getOriginalFilename() + "成功");
            String filename = multipartFile.getOriginalFilename();
            //获取当前所在目录
            String currentPath = ftpClient.printWorkingDirectory();
            LOGGER.info("获取当前所在目录" + currentPath);

            //如果当前目录不是目标目录
            if (folder != null) {
                if (!currentPath.equals(home + folder)) {
                    //判断创建目录
                    if (!makeFolder(folder)) {
                        throw new IOException("创建目录失败，可能是没有改账号没有权限");
                    }
                }
            } else {
                ftpClient.changeWorkingDirectory(home);
            }
            //转换为file类型
            inputStream = multipartFile.getInputStream();

//            ftpClient.setBufferSize(1024 * 1024);
//            byte[] fileBytes = multipartFile.getBytes();
//            inputStream = new BufferedInputStream(new ByteArrayInputStream(fileBytes));
            //检测文件是否存在，存在则重新命名
//            String createFileName = createFileName(filename);
            String createName = generateUUID();
            //获取文件的后缀：如.jpg
            String suffix = filename.substring(filename.lastIndexOf("."));
            String createFileName = createName+ suffix;


            LOGGER.info("开始上传文件" + createFileName);
            boolean storeFile = ftpClient.storeFile(new String(createFileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET), inputStream);
            LOGGER.info("正在关闭流" + createFileName);
            inputStream.close();
            ftpClient.logout();
            if (storeFile) {
                if (folder != null) {
                    LOGGER.info(createFileName + "上传文件成功：" + server + "/" + folder + "/" + createFileName);
                    return server + home + folder + "/" + createFileName;
                } else {
                    LOGGER.info(createFileName + "上传文件成功：" + server + "/" + createFileName);
                    return server + home + createFileName;
                }
            } else {
                LOGGER.info("上传文件失败");
                return null;
            }

        } catch (Exception e) {
            System.out.println("上传文件失败");
            e.printStackTrace();
        } finally {
            //关闭连接
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //关闭流
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public static String generateUUID() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        Random random = new Random();
        String unique = random.nextInt(89999 + 10000) + simpleDateFormat.format(new Date());
        return unique;
    }

    /**
     * 检查文件是否存在
     * @param filename 文件名
     * @return
     * @throws IOException
     */
    private boolean isFileExits(String filename) throws IOException {
        InputStream is = ftpClient.retrieveFileStream(new String(filename.getBytes(LOCAL_CHARSET),FTP.DEFAULT_CONTROL_ENCODING));
        if(is == null || ftpClient.getReplyCode() == FTPReply.FILE_UNAVAILABLE){
            return false;
        }

        if(is != null){
            is.close();
            ftpClient.completePendingCommand();
        }
        return true;
    }


    /**
     * ftp关闭
     *
     * @param ftpClient ftp实例
     */
    public void close(FTPClient ftpClient) {
        try {
            if (ftpClient != null) {
                ftpClient.logout();
            }
        } catch (Exception e) {
            LOGGER.error("FTP退出登录失败");
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException ioe) {
                    LOGGER.error("FTP关闭失败");
                }
            }
        }

    }

    /**
     * ftp删除文件
     *
     * @param filePath 文件地址
     * @return 是否成功
     */
    public boolean delete(String filePath) {
        isConnect();
        if (!filePath.startsWith(server)) {
            throw new BaseException("文件地址错误");
        }
        String[] splitPaths = filePath.split(server);
        String path = splitPaths[splitPaths.length - 1];
        try {
            ftpClient.deleteFile(path);
            return true;
        } catch (IOException e) {
            LOGGER.info("文件删除异常：" + e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * ftp下载文件
     *
     * @param filePath 文件地址
     * @return 是否成功
     */
    public void download(String filePath) {
        isConnect();
        if (!filePath.startsWith(server)) {
            throw new BaseException("文件地址错误");
        }
        String[] splitPaths = filePath.split(server);
        String path = splitPaths[splitPaths.length - 1];
        try {
            File file = new File(this.path + path);
            OutputStream os = new FileOutputStream(file);
            ftpClient.retrieveFile(file.getName(), os);
            os.close();
        } catch (FileNotFoundException e) {
            System.out.println("没有找到" + filePath + "文件");
            e.printStackTrace();
        } catch (SocketException e) {
            System.out.println("连接FTP失败.");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件读取错误。");
            e.printStackTrace();
        }
    }


    /**
     * 判断连接，没有连接就重新连接
     */
    private void isConnect() {
        if (!ftpClient.isConnected()) {
            connect();
        }
    }


    /**
     * 创建文件夹
     *
     * @param folder 文件夹路径
     */
    public boolean makeFolder(String folder) {
        //切换的目录
        String contents = null;
        if (folder == null && Objects.equals(home, "/")) {
            contents = home;
        } else if (Objects.equals(home, "/")) {
            contents = home + folder;
        } else if (!Objects.equals(home, "/")) {
            contents = home + "/" + folder;
        }
        isConnect();

        //如果切换目录失败，就创建这个目录
        try {
            if (!ftpClient.changeWorkingDirectory(contents)) {
                //如果目录是逐层多级
                if (!Objects.equals(contents, home) && contents.split("/").length > 1) {
                    return createDirs(contents);
                }
                try {
                    LOGGER.info("切换目录失败，正在切换到家目录");
                    ftpClient.changeWorkingDirectory(home);
                    //如果创建目录失败就报错
                    LOGGER.info("切换家目录成功，正在创建新目录" + contents);
                    if (!ftpClient.makeDirectory(new String(contents.getBytes(LOCAL_CHARSET), SERVER_CHARSET))) {
                        return false;
                    }
                    //再次切换到上传目录
                    ftpClient.changeWorkingDirectory(contents);
                    LOGGER.info("切换目的目录" + contents + "成功");
                    return true;
                } catch (IOException e) {
                    LOGGER.info("切换目录：" + contents + "失败");
                    e.printStackTrace();
                    return false;
                }

            }
            LOGGER.info("切换目的目录" + contents + "成功");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 创建多级目录
     *
     * @param path 可能需要创建的多级目录路径，/分隔
     */
    public boolean createDirs(String path) {
        /*该部分为逐级创建*/
        String[] split = path.split("/");

        try {
            for (String str : split) {
                if (StringUtils.isBlank(str)) {
                    continue;
                }
                if (!ftpClient.changeWorkingDirectory(str)) {
                    LOGGER.info("FTP: 目录不存在");
                    boolean makeDirectory = ftpClient.makeDirectory(new String(str.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
                    boolean changeWorkingDirectory = ftpClient.changeWorkingDirectory(str);
                    LOGGER.info(str + "FTP： 创建结果：" + makeDirectory + ";切换工作目录结果：" + changeWorkingDirectory);
                } else {
                    LOGGER.info("目录已存在");
                }
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.warn(e.getMessage());
            return false;
        }
    }


    /**
     * 根据上传的文件名判断是否存在相同的文件名，若存在则(i++)的方式命名
     * @param originalFilename 文件名
     * @return 返回原始文件名或者(i++)文件名形式，如：123(1).txt
     */
    public String createFileName(String originalFilename) {
        //获取文件的前缀名
        String frontName = originalFilename.substring(0, originalFilename.lastIndexOf("."));
        //获取文件的后缀：如.jpg
        String backName = originalFilename.substring(originalFilename.lastIndexOf("."));
        int i = 1;
        String fileName = originalFilename;
        //若文件存在重命名
        try {
            while (isFileExits(fileName)){
                fileName = frontName + "(" + i + ")" + backName;
                i++;
            }
            return fileName;
        } catch (IOException e) {
            e.printStackTrace();
            return originalFilename;
        }

    }


    /**
     * 获取文件列表
     *
     * @param folders 路径
     * @return 列表集合
     */
    public List<FTPFile> showFolder(String folders) {
        folders = folders == null ? home : folders;
        isConnect();
        List<FTPFile> list = null;
        try {
            FTPFile[] ftpFiles = ftpClient.listFiles(folders);
            if (ftpFiles != null && ftpFiles.length > 0) {
                list = new ArrayList<>(ftpFiles.length);
                Collections.addAll(list, ftpFiles);
            } else {
                list = new ArrayList<>(0);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

}
